package com.syh.jn.scm.service.impl.supplier.logistics;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.alibaba.dubbo.config.annotation.Service;
import com.syh.core.ddd.event.annotation.DomainEventHandle;
import com.syh.core.ddd.infrastructure.repository.AggregateRepository;
import com.syh.core.ddd.service.AbstractServiceImpl;
import com.syh.jn.scm.domain.downstream.entity.logistics.DownLogisticsBill;
import com.syh.jn.scm.domain.downstream.event.DownLogisticsBillEvent;
import com.syh.jn.scm.domain.supplier.entity.finance.StatementAccountBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBillItem;
import com.syh.jn.scm.domain.supplier.event.LogisticsBillEvent;
import com.syh.jn.scm.domain.supplier.event.finance.StatementAccountBillEvent;
import com.syh.jn.scm.domain.supplier.repository.logistics.LogisticsBillRepository;
import com.syh.jn.scm.domain.upstream.entity.logistics.UpLogisticsBill;
import com.syh.jn.scm.domain.upstream.event.UpLogisticsBillEvent;
import com.syh.jn.scm.dto.supplier.logistics.LogisticsBillItemVo;
import com.syh.jn.scm.dto.supplier.logistics.LogisticsBillMto;
import com.syh.jn.scm.dto.supplier.logistics.LogisticsBillSto;
import com.syh.jn.scm.dto.supplier.logistics.LogisticsBillVo;
import com.syh.jn.scm.service.api.supplier.finance.enums.StatementAccountBillEnum;
import com.syh.jn.scm.service.api.supplier.logistics.LogisticsBillService;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by soci.song on 2018/11/2.
 */
@Service
@Transactional
public class LogisticsBillServiceImpl extends AbstractServiceImpl<LogisticsBillSto, LogisticsBillMto, LogisticsBillVo, LogisticsBill, Long> implements LogisticsBillService {
  @Autowired
  private LogisticsBillRepository logisticsBillRepository;

  @Override
  protected AggregateRepository<LogisticsBill, Long> getAggregateRepository() {
    return logisticsBillRepository;
  }

  /**
   * 创建物流单
   *
   * @param logisticsBillSto
   * @return
   */
  @Override
  public Long save(LogisticsBillSto logisticsBillSto) {
    LogisticsBill logisticsBill = new LogisticsBill(logisticsBillSto);
    Long id = logisticsBillRepository.add(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillCreated(new Object[]{logisticsBill}));
    //当不是物流发货时,自动出车
    if (!logisticsBillSto.getDeliveryType().equals(LogisticsBillEnums.DeliveryType.LOGISTICS_TYPE.getDeliveryType())) {
      this.delivery(id,logisticsBill.getDeliveryTimestamp());
    }
    return id;
  }

  /**
   * 修改物流单
   *
   * @param mto
   */
  @Override
  public void update(LogisticsBillMto mto) {
    LogisticsBill logisticsBill = judgeNull(mto.getId());
    logisticsBill.modify(mto);
    logisticsBillRepository.update(logisticsBill);
  }

  /**
   * 直采直销修改运费承担方
   * @param id 物流单id
   * @param type 运费承担方式  1、卖家承担 2、预收买家 3、买家到付
   */
  @Override
  public void changeFreightType(Long id,Short type) {
    LogisticsBill logisticsBill = judgeNull(id);
    logisticsBill.changeFreightType(type);
    logisticsBillRepository.updateStatus(logisticsBill);
  }

  /**
   * 发货
   *
   * @param id
   */
  @Override
  public void delivery(Long id, Long deliveryTimestamp) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(id);
    logisticsBill.delivery(deliveryTimestamp);
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillDelivery(new Object[]{logisticsBill}));
  }

  /**
   * 收货
   *
   * @param id
   */
  @Override
  public void collect(Long id, Long depotId, Long storageTimestamp) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(id);
    logisticsBill.collectDepot(depotId,storageTimestamp);
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillCollected(new Object[]{logisticsBill}));
  }

  @Override
  public void cancel(Long id) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(id);
    LogisticsBill oldBill = logisticsBillRepository.getById(id);
    logisticsBill.cancel();
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillCancelled(new Object[]{logisticsBill,oldBill}));
  }

  /**
   * 消费者(监听上游取消签收)
   *
   * @param upLogisticsBill
   */
  @DomainEventHandle(UpLogisticsBillEvent.UP_LOGISTICS_BILL_UN_RECEIVED)
  public void handelLogisticsUnReceived(UpLogisticsBill upLogisticsBill,UpLogisticsBill oldBill) {
    //当不是物流发货时才自动的取消出车
    if (!upLogisticsBill.getDeliveryType().equals(LogisticsBillEnums.DeliveryType.LOGISTICS_TYPE.getDeliveryType())) {
      this.cancel(upLogisticsBill.getId());
    }
  }

  /**
   * 消费者(监听下游取消收货)
   *
   * @param downLogisticsBill
   */
  @DomainEventHandle(DownLogisticsBillEvent.DOWN_LOGISTICS_BILL_UN_RECEIVED)
  public void handelLogisticsCancel(DownLogisticsBill downLogisticsBill,DownLogisticsBill oldBill) {
    //当不是物流发货 且不是直采直销物流单时才自动的取消出车
    if (!downLogisticsBill.getDeliveryType().equals(LogisticsBillEnums.DeliveryType.LOGISTICS_TYPE.getDeliveryType())
            && downLogisticsBill.getSubjectBillId() == null
    ) {
      this.cancel(downLogisticsBill.getId());
    }
  }

  /**
   * 取消收货
   *
   * @param id
   */
  @Override
  public void cancelReceipt(Long id) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(id);
    LogisticsBill oldBill = logisticsBillRepository.getById(id);
    logisticsBill.cancelReceipt();
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillUnReceived(new Object[]{logisticsBill,oldBill}));
  }

  @Override
  public void remove(Long id) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(id);
    //如果不是物流发货 且是已发货状态 先取消发货
    if (!logisticsBill.getDeliveryType().equals(LogisticsBillEnums.DeliveryType.LOGISTICS_TYPE.getDeliveryType())
            && logisticsBill.getLogisticsState().equals(LogisticsBillEnums.State.DELIVEED.getStatus())) {
      this.cancel(id);
      logisticsBill = logisticsBillRepository.getById(id);
    }
    logisticsBill.remove();
    logisticsBillRepository.removeById(id);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillDeleted(new Object[]{logisticsBill}));
  }

  /**
   * 消费者(监听对账单创建)
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_CREATED)
  public void handelStatementAccountBillCreated(StatementAccountBill bill) {
    bill.getStatementAccountBillItems().forEach(statementAccountBillItem -> {
      if (statementAccountBillItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_LIGISTICS_BILL.getStatus())) {
        LogisticsBill logisticsBill = logisticsBillRepository.getById(statementAccountBillItem.getSourceId());
        logisticsBill.generating(bill.getId());
        logisticsBillRepository.updateStatus(logisticsBill);
      }
    });
  }

  /**
   * 消费者(监听对账单修改)
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_UPDATA)
  public void handelStatementAccountBillUpdata(StatementAccountBill bill) {
    Wrapper wrapper = Wrapper.build();
    wrapper.append(Restrictions.eq("statementId", bill.getId()));
    List<LogisticsBill> logisticsBills = logisticsBillRepository.getList(wrapper);
    logisticsBills.forEach(logisticsBill -> {
      logisticsBill.cancelGenerating();
      logisticsBillRepository.updateStatus(logisticsBill);
    });
    bill.getStatementAccountBillItems().forEach(statementAccountBillItem -> {
      if (statementAccountBillItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_LIGISTICS_BILL.getStatus())) {
        LogisticsBill logisticsBill = logisticsBillRepository.getById(statementAccountBillItem.getSourceId());
        logisticsBill.generating(bill.getId());
        logisticsBillRepository.updateStatus(logisticsBill);
      }
    });
  }


  /**
   * 消费者：监听对账单（删除对账单状态和对账单号）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_DESTROY)
  protected void handleStatementBillAccountDestroy(StatementAccountBill bill) {
    bill.getStatementAccountBillItems().forEach(statementAccountBillItem -> {
      if (statementAccountBillItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_LIGISTICS_BILL.getStatus())) {
        LogisticsBill logisticsBill = logisticsBillRepository.getById(statementAccountBillItem.getSourceId());
        logisticsBill.cancelGenerating();
        logisticsBillRepository.updateStatus(logisticsBill);
      }
    });
  }

  /**
   * 消费者：监听直采物流单创建（创建直销物流单）
   *
   * @param upLogisticsBill
   */
  @DomainEventHandle(UpLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_CREATED)
  protected void handlePurchaseSaleLogisticsBillCreated(UpLogisticsBill upLogisticsBill) {
    LogisticsBill logisticsBill = new LogisticsBill(upLogisticsBill);
    logisticsBillRepository.add(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillCreated(new Object[]{logisticsBill}));
  }

  /**
   * 消费者：监听直采物流单修改（修改直销物流单）
   *
   * @param upLogisticsBill
   */
  @DomainEventHandle(UpLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_UPDATED)
  protected void handlePurchaseSaleLogisticsBillUpdated(UpLogisticsBill upLogisticsBill) {
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("subjectBillId", upLogisticsBill.getId()));
    LogisticsBill logisticsBill = logisticsBillRepository.getOne(we);
    logisticsBill.modifySecondaryBill(upLogisticsBill);
    logisticsBillRepository.update(logisticsBill);
  }

  /**
   * 消费者：监听直采物流单发货（直销物流单发货）
   *
   * @param upLogisticsBill
   */
  // 因物流费用不同  取消直采直销发货同步
//  @DomainEventHandle(UpLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_DELIVERY)
  protected void handlePurchaseSaleLogisticsBillDelivery(UpLogisticsBill upLogisticsBill) {
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("subjectBillId", upLogisticsBill.getId()));
    LogisticsBill logisticsBill = logisticsBillRepository.getOne(we);
    logisticsBill.deliverySecondaryBill();
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillDelivery(new Object[]{logisticsBill}));
  }

  /**
   * 消费者：监听直采物流单收货（直销物流单收货）
   *
   * @param downLogisticsBill
   */
  @DomainEventHandle(DownLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_COLLECTED)
  protected void handlePurchaseSaleLogisticsBillCollected(DownLogisticsBill downLogisticsBill) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(downLogisticsBill.getSubjectBillId());
    logisticsBill.setDepotId(downLogisticsBill.getDepotId());
    logisticsBill.collect(downLogisticsBill.getStorageTimestamp());
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillCollected(new Object[]{logisticsBill}));
  }

  /**
   * 消费者：监听直采物流单取消发货（直销物流单取消）
   *
   * @param upLogisticsBill
   */
  @DomainEventHandle(UpLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_CANCELLED)
  protected void handlePurchaseSaleLogisticsBillCancelled(UpLogisticsBill upLogisticsBill) {
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("subjectBillId", upLogisticsBill.getId()));
    LogisticsBill logisticsBill = logisticsBillRepository.getOne(we);
    LogisticsBill oldBill = logisticsBillRepository.getOne(we);
    //如果销售单物流未发车 则不需要同步
    if (logisticsBill.getLogisticsState().equals(upLogisticsBill.getLogisticsState())) {
      return;
    }
    logisticsBill.cancelSecondaryBill();
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillCancelled(new Object[]{logisticsBill,oldBill}));
  }

  /**
   * 消费者：监听直采物流单取消收货（直销物流单取消）
   *
   * @param downLogisticsBill
   */
  @DomainEventHandle(DownLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_UN_RECEIVED)
  protected void handlePurchaseSaleLogisticsBillUnReceived(DownLogisticsBill downLogisticsBill) {
    LogisticsBill logisticsBill = logisticsBillRepository.getById(downLogisticsBill.getSubjectBillId());
    LogisticsBill oldBill = logisticsBillRepository.getById(logisticsBill.getId());
    logisticsBill.cancelReceiptSubjectBill();
    logisticsBillRepository.updateStatus(logisticsBill);
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillUnReceived(new Object[]{logisticsBill,oldBill}));
  }

  /**
   * 消费者：监听直采物流单删除（删除直销物流单）
   *
   * @param id
   */
  @DomainEventHandle(UpLogisticsBillEvent.PURCHASE_SALE_LOGISTICS_BILL_DELETED)
  protected void handlePurchaseSaleLogisticsBillDeleted(Long id) {
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("subjectBillId", id));
    LogisticsBill logisticsBill = logisticsBillRepository.getOne(we);
    logisticsBill.removeSenondaryBill();
    logisticsBillRepository.removeById(logisticsBill.getId());
    this.publicEvent(LogisticsBillEvent.bulidLogisticsBillDeleted(new Object[]{logisticsBill}));
  }

  /**
   * 判空方法
   *
   * @param id
   * @return
   */
  private LogisticsBill judgeNull(Long id) {
    ExtAssert.notNull(id, "id不能为空");
    LogisticsBill entity = logisticsBillRepository.getById(id);
    ExtAssert.notNull(entity, "订单不存在");
    return entity;
  }

  @Override
  public List<LogisticsBillItemVo> getItemsByLogisticsBillId(Long id) {
    return transformLogistics(logisticsBillRepository.getItemsByLogisticsBillId(id));
  }

  @Override
  public List<LogisticsBillVo> getLogisticsBillVosByOrderId(Long id, Short orderType) {
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("orderType", orderType));
    we.append(Restrictions.eq("orderId", id));
    List<LogisticsBillVo> billVos = logisticsBillRepository.getListView(we);
    billVos.forEach(vo -> {
      List<LogisticsBillItemVo> itemVos = transformLogistics(logisticsBillRepository.getItemsByLogisticsBillId(vo.getId()));
      vo.setItems(itemVos);
    });
    return billVos;
  }

  /**
   * 内部方法  领域对象转vo
   *
   * @param entityList
   * @return
   */
  private List<LogisticsBillItemVo> transformLogistics(List<LogisticsBillItem> entityList) {
    List<LogisticsBillItemVo> voList = new ArrayList<>();
    entityList.stream().forEach((entity) -> {
      LogisticsBillItemVo vo = new LogisticsBillItemVo();
      ExtBeanUtils.copyProperties(entity, vo);
      voList.add(vo);
    });
    return voList;
  }

  private LogisticsBillItemVo trans(LogisticsBillItem entity) {
    LogisticsBillItemVo vo = new LogisticsBillItemVo();
    ExtBeanUtils.copyProperties(entity, vo);
    return vo;
  }
}
